# ----------------------------------------------------------------------------
#       Copyright (C) 2013-2014 Huynh Vi Lam  <domovilam@gmail.com>
#
#       This file is part of pimucha.
#
#	This program is free software: you can redistribute it and/or modify
#	it under the terms of the GNU General Public License as published by
#	the Free Software Foundation, either version 3 of the License, or
#	(at your option) any later version.
#	
#	This program is distributed in the hope that it will be useful,
#	but WITHOUT ANY WARRANTY; without even the implied warranty of
#	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#	GNU General Public License for more details.
#	
#	You should have received a copy of the GNU General Public License
#	along with this program.  If not, see <http://www.gnu.org/licenses/>.
# ----------------------------------------------------------------------------

import logging,sys,threading
if sys.hexversion > 0x030000F0:
    import queue as Queue
else:
    import Queue
QEmpty = Queue.Empty
from piHAlibs import (my_fctname,coroutine,db2dict,tstampdev,unitid)
from .x10libs import *

logger = logging.getLogger()
		

@my_fctname
@coroutine	
def x10plcdecoder(targets,rejected=None,newids=None,rawevt=None):
    """
    Decode an X10 complete event receiving from CM11a over Powerline
	Only for event with ON / OFF function
	item = ['CM0:CM11','0x02 0x00 0x68 0x02 0x01 0x62'] (X10 cmd = A14 ON)
        msgs = ['CM0:CM11', 'x10', ['0x02', '0x00', '0x68', '0x02', '0x01', '0x62'], [2, 0, 104, 2, 1, 98], 'NC','0x5a','0x02 0x00 0x68 0x02 0x01 0x62']
    """
    condition = " WHERE typeunit = 'device'"
    ids = db2dict('UNITIDS',condition)
    while True:
        msgs = (yield)
        logger.debug("Msgs received %s",repr(msgs))
        # msgs = [namedev, protocol, data, packet, model]
        namedev, protocol, data, p, model,hd,revt = msgs
        if hd != '0x5a': continue
       	if len(data) != 6: continue
       	logger.debug("Check for x10 PL with these values %s",repr(p))
        try:				#Decode house + unit
            hu = hex2hu(data[2])	#A14
        except:
            logger.error('Error in decoding %s',repr(data))
       	    if rejected: rejected.send(['FAIL_DECODE_huf'] + msgs)
       	    continue
        hfb = x10decode1(data[3:])	#Decode house + function
        if hfb:
            hf,b,f = hfb		#[['A', 'ON'], 'ON', 2]
        else:
            logger.debug("Function x10plcdecoder unable to decode %s",repr(data))
       	    if rejected: rejected.send(['FAIL_DECODE_huf'] + msgs)
            continue
        if hu[0] != hf[0]:
            logger.info( "House code of data : %s is different with %s", hu[0], hf[0])
       	    if rejected: rejected.send(['DIFF_DECODE_huf'] + msgs)
       	    continue
        decevt = [protocol,hu,b]
        event = tstampdev(namedev) + ['device'] + decevt
        for target in targets:
            logger.debug("Function x10plcdecoder for %s",repr(event))
            target.send(event)
        #Receiver Unit / addr
        if (protocol, hu) not in ids:
            logger.debug("New device X10 detected with unit %s", hu)
            if newids: ids = unitid([protocol,model,'device',hu])
        if rawevt: rawevt.send([namedev,revt] + decevt)


@my_fctname
@coroutine	
def x10pldecoder(targets,rejected=None,newids=None,rawevt=None):
    """
    Decode an X10 partial event receiving from CM11a or CM15a over Powerline
    """
    hq = Queue.Queue()
    condition = " WHERE typeunit = 'device'"
    ids = db2dict('UNITIDS',condition)
    while True:
        msgs = (yield)
       	logger.debug("Msgs received %s",repr(msgs))
        namedev, protocol, data, p, model,hd,revt = msgs
        if hd != '0x5a': continue
        if hq.empty():
            hu = None					#House + Unit
        nb = len(data)
        if nb > 4: continue
        if nb == p[0] + 1:
            if p[1] == 0:			#Evt with house + unit
                try:
                    a = hex2hu(data[2])
                except:
                    logger.error('Error in decoding %s',repr(data))
                    if rejected: rejected.send(['FAIL_DECODE_huf'] + msgs)
                    continue
                if hu != a:
                    flushx10evt(targets,hq)
                hu = a
                event = tstampdev(namedev) + ['device',protocol,hu,'NA']
                timerx10evt(targets,hq,event)
            elif p[1] == 1:
                hfb = x10decode1(data)
                if not hfb:
                    flushx10evt(targets,hq)
                    logger.debug("Unable to decode %s",repr(data))
                    if rejected: rejected.send(['FAIL_DECODE_huf'] + msgs)
                    continue
                hf,b,f = hfb
                if not hu:
                    a = hf[0]
                else:
                    if hu[0] != hf[0]:
                        flushx10evt(targets,hq)
                        logger.info( "House code of previous event : %s is different with %s", hu[0], hf[0])
                        logger.info( "Event X10 : %s", repr(data))
                        continue
                    a = hu
                postx10evt(targets,hq,a,b,ids,newids,rawevt,msgs)
            elif p[1] == 2:			#house + unit  - house + function
                huhf = x10decode2(namedev.split(':')[1],data)
                if not huhf:
                    flushx10evt(targets,hq)
                    logger.debug("Unable to decoded %s",repr(data))
                    if rejected: rejected.send(['FAIL_DECODE_huf'] + msgs)
                    continue
                if huhf[0]:
                    hu = huhf[0]
                hf = huhf[1]
                postx10evt(targets,hq,hu,hf[1],ids,newids,rawevt,msgs)
        else:
            logger.warning('First value read %s for the sequence differs with the length %s of incoming event',data[0],nb)
       	    if rejected: rejected.send(['DIFF_DECODE_huf'] + msgs)



def flushx10evt(targets,hq):
    if not hq.empty():
        evt = hq.get_nowait()
        for target in targets:
            logger.debug("Function x10pldecoder for incomplete event %s",repr(evt))
            target.send(evt)
    return


def timerx10evt(targets,hq,evt):
    try:
        hq.put_nowait(evt)
    except:
        return
    tt = threading.Timer(3,flushx10evt,[targets,hq])
    tt.name = 'Timer_x10pldecoder_' + evt[5].lower()
    tt.start()
    return


def postx10evt(targets,hq,hu,evtf,ids,newids,rawevt,msgs):
    namedev, protocol, data, p, model,hd,revt = msgs
    decevt = [protocol,hu,evtf]
    event = tstampdev(namedev) + ['device'] + decevt
    for target in targets:
        logger.debug("Function x10pldecoder for %s",repr(event))
        target.send(event)
    #New Unit / addr
    if (protocol,hu) not in ids:
        logger.debug("New device X10 detected with unit %s", hu)
        if newids: ids = unitid([protocol,model,'device',hu])
    if rawevt: rawevt.send([namedev,revt] + decevt)
    #Purge hq
    try:
        evt = hq.get_nowait()
    except QEmpty:
        pass
        evt = event
    #Cancel Timer
    ths = threading.enumerate()
    for th in ths:
        if 'Timer_x10pldecoder_' + evt[5] in repr(th): th.cancel()
    return
